package de.iritgo.skillfull.director;

import java.awt.Point;
import java.awt.Rectangle;
import java.util.Random;

import org.newdawn.slick.GameContainer;
import org.newdawn.slick.Input;

import de.iritgo.skillfull.collision.CollisionEvent;
import de.iritgo.skillfull.component.visual.VirusComponent;
import de.iritgo.skillfull.config.ConfigManager;
import de.iritgo.skillfull.entity.Command;
import de.iritgo.skillfull.entity.Entity;
import de.iritgo.skillfull.entity.EntityIdleEvent;
import de.iritgo.skillfull.entity.EntityManager;
import de.iritgo.skillfull.entity.command.AnimationCommand;
import de.iritgo.skillfull.entity.command.BarCommand;
import de.iritgo.skillfull.entity.command.MoveFieldCommand;
import de.iritgo.skillfull.entity.command.PauseCommand;
import de.iritgo.skillfull.entity.command.PlaySoundCommand;
import de.iritgo.skillfull.entity.command.PlayerMovementCommand;
import de.iritgo.skillfull.entity.command.RemoveEntityCommand;
import de.iritgo.skillfull.entity.command.SetPositionCommand;
import de.iritgo.skillfull.entity.command.SimpleTextCommand;
import de.iritgo.skillfull.event.Event;
import de.iritgo.skillfull.event.EventListener;
import de.iritgo.skillfull.event.EventService;

public class DirectorImpl implements Director, EventListener 
{
	private EntityManager entityManager;
	private Random generator = new Random ();
	private int timeline = 0;
	private int attackStart = 0;
	private AttackStrategy attackStrategy;
	private int heroHealth = 100;
	private int agro = 1;
	private int timeLeft = 1000 * 60 * 3;
//	private int timeLeft = 1000 * 5;
	private ConfigManager configManager;
	private int agroStep = 4;
	private boolean gameover = false;
	private boolean won = false;
	private int goAttack = 1000 * 40;
	
	public void setEntityManager(EntityManager entityManager) 
	{
		this.entityManager = entityManager;
	}
	
	public void setConfigManager(ConfigManager configManager) 
	{
		this.configManager = configManager;
	}

	/**
	 * Can't init with spring...
	 * 
	 * @return The attack strategy
	 */
	public AttackStrategy getAttackStrategy ()
	{
		if (attackStrategy == null)
			attackStrategy = new AttackStrategy ();
		return attackStrategy;
	}
	
	
	@Override
	public void update (GameContainer container, int delta) 
	{
		if (heroHealth < 1)
		{
			heroHealth = 0;
			gameover  = true;
			Entity gameoverEntity = entityManager.getEntity ("gameover");
			gameoverEntity.addCommand (new SimpleTextCommand (gameoverEntity, "GAME OVER - Please restart the game!", Integer.MAX_VALUE, true));
			return;
		}
		if (timeLeft < 0 && ! gameover && ! won)
		{
			won = true;
			int x = 1280;
			
			for (int y = 0; y < 32 ; ++y)
			{
				Entity troop = entityManager.getPrototypeEntity ("troop");
				troop.setName ("troop" + entityManager.getNextId ());
				troop.addCommand (new SetPositionCommand (troop, x - 32, y * 42, false));
				for (int i = 0 ; i < 11 ; ++i)
					troop.addCommand (new MoveFieldCommand (troop, Entity.DST_FIELD.WEST));
				entityManager.addEntity (troop);
				
				Entity shot = entityManager.getPrototypeEntity ("shot");
				shot.setName ("troop" + entityManager.getNextId ());
				shot.addCommand (new SetPositionCommand (shot, x - 32, y * 42, false));
				for (int i = 0 ; i < 100 ; ++i)
					shot.addCommand (new MoveFieldCommand (shot, Entity.DST_FIELD.WEST));
				entityManager.addEntity (shot);
			}
			
			Entity troop = entityManager.getPrototypeEntity ("troop");
			troop.setName ("troop" + entityManager.getNextId ());
			troop.addCommand (new SetPositionCommand (troop, 1280 - 80, 800, false));
			troop.addCommand (new MoveFieldCommand (troop, new Point (700,500)));
			troop.addCommand (new SimpleTextCommand (troop, "Good job bob!", 1000, true));

			Entity hero = entityManager.getEntity ("hero");
			hero.addCommand (new MoveFieldCommand (hero, new Point (620,500)));
			hero.addCommand (new MoveFieldCommand (hero, Entity.DST_FIELD.EAST));
			hero.addCommand (new PauseCommand (hero, 4500));

			troop.addCommand (new SimpleTextCommand (troop, "Good job Bob!", 2000, true));
			troop.addCommand (new SimpleTextCommand (hero, "Thank you, SIR!", 2000, true));
			troop.addCommand (new SimpleTextCommand (troop, "Is the beer ok?", 2000, true));
			troop.addCommand (new SimpleTextCommand (hero, "I have done my best one, SIR!", 2000, true));
			troop.addCommand (new SimpleTextCommand (troop, "Hmmmmmm.....", 2000, false));
			troop.addCommand (new PlaySoundCommand (Sounds.mhhBeer));
			troop.addCommand (new PauseCommand (troop, 2500));
			troop.addCommand (new PlaySoundCommand (Sounds.openBeer));
			troop.addCommand (new PauseCommand (troop, 1500));
			troop.addCommand (new SimpleTextCommand (hero, "NNNNOOOOOOOOOOoooooooooo...", 2000, true));
			troop.addCommand (new SimpleTextCommand (hero, "You have won - start again ;-)", Integer.MAX_VALUE, true));

			entityManager.addEntity (troop);

		}
		if (won)
			return;
		
		attackStart += delta;
		if (attackStart > goAttack)
		{
			heroInteraction (container, delta);
			timeline += delta;
			timeLeft -= delta;
			
			if (timeline > 5000)
			{
				switch (generator.nextInt (4) + 1)
				{
					case 1:
					{
						getAttackStrategy ().attackTop (heroHealth, timeLeft);
						break;
					}
					case 2:
					{
						getAttackStrategy ().attackRight (heroHealth, timeLeft);
						break;
					}
					case 3:
					{
						getAttackStrategy ().attackBottom (heroHealth, timeLeft);
						break;
					}
					case 4:
					{
						getAttackStrategy ().attackLeft (heroHealth, timeLeft);
						break;
					}
				}
				timeline = 0;
			}
			Entity agroEntity = entityManager.getEntity ("agro");
			if (agro >= 100)
			{
				agroEntity.addCommand (new BarCommand (agroEntity, "A", 35, 350, Integer.MAX_VALUE));
			}
			else
			{
				int rheight = (360 / 100) * agro; 
				agroEntity.addCommand (new BarCommand (agroEntity, "A", 35, rheight, Integer.MAX_VALUE));
			}
		}
	}
	
	public void event (Event event) 
	{
		if (event.getEventType() == EventService.EVENT_TYPE.COLLISION)
		{
			CollisionEvent col = (CollisionEvent) event;
			
			Entity entity1 = col.getEntity1 (); 
			Entity entity2 = col.getEntity2 ();
			
			if (entity2.isDeactivated() || entity1.isDeactivated())
				return;
			
			if ( (entity1.getId().equals("hero") && (entity2.getId().equals("alien1") || entity2.getId().equals("alien2"))) ||
				 (entity2.getId().equals("hero") && (entity1.getId().equals("alien1") || entity1.getId().equals("alien2"))))
			{
				if (won)
				{
					return;
				}
				heroHealth -= 10;
				Entity health = entityManager.getEntity ("health");
				int rheight = (350 / 100) * heroHealth; 
				health.addCommand(new BarCommand (health, "H", 35, rheight, Integer.MAX_VALUE));
//				System.out.println ("Health: " + heroHealth);
				return;
			} 

			if (entity1.getId().equals("hero") && entity2.getId().equals("shot"))
			{
				return;
			} 
			else if (entity1.getId().equals("shot") && entity2.getId().equals("shot"))
			{
				return;
			}
			if ((entity1.getId().equals("alien1") && entity2.getId().equals("shot")) ||
			    (entity1.getId().equals("shot") && entity2.getId().equals("alien1")))
			{
				if (entity1.getId ().equals ("shot"))
				{
					killShot (entity1);
					killAlien (entity2);
				}
				if (entity2.getId ().equals ("shot"))
				{
					killShot (entity2);
					killAlien (entity1);
				}
				return;
			}
			if ((entity1.getId().equals("alien2") && entity2.getId().equals("shot")) ||
					(entity1.getId().equals("shot") && entity2.getId().equals("alien2")))
			{
				if (entity1.getId ().equals ("shot"))
				{
					killShot (entity1);
					killAlien (entity2);
				}
				if (entity2.getId ().equals ("shot"))
				{
					killShot (entity2);
					killAlien (entity1);
				}
        		return;
			}
			
			VirusComponent virus1 = (VirusComponent) entity1.getComponentById ("VirusComponent");
			VirusComponent virus2 = (VirusComponent) entity2.getComponentById ("VirusComponent");
			if (virus1 != null && virus2 != null && (virus1.hasTheVirus () || virus2.hasTheVirus()))
			{
				virus1.setYouHaveTheVirusNow ();
				virus2.setYouHaveTheVirusNow ();
			}

			undoCommand (entity1);
//			undoCommand (entity2);
			
			Entity entity = entity2;
			Command command = entity.getCurrentCommand ();
			if (command == null)
				return;
			Command undoCommand = command.getUndoCommand();
			command.stop ();
			entity.removeCommand (command);
			entity.addCommand (new MoveFieldCommand (entity, MoveFieldCommand.STOP));
			entity.addCommand (new PauseCommand (entity, 2000));
			entity.addCommand (undoCommand);
			
		}
		else if (event.getEventType() == EventService.EVENT_TYPE.IDLE)
		{
			if (won)
				return;
			
			EntityIdleEvent idle = (EntityIdleEvent) event;
			Entity entity = idle.getEntity ();
			if (entity.getId().equals("hero") || entity.getId().equals("shot") || entity.getId().equals("health") ||
					entity.getId().equals("agro") ||
					entity.getName().equals("gameover"))
				return;

			if (entity.getName ().equals ("timeLeft"))
			{
				if (attackStart <= goAttack)
					return;
				
				Entity timeLeft = entityManager.getEntity ("timeLeft");
				if (this.timeLeft < (1000 * 70))
					timeLeft .addCommand (new SimpleTextCommand (timeLeft, "Reinforcements in: " + this.timeLeft / (1000) + "s", 1000, true));
				else
					timeLeft .addCommand (new SimpleTextCommand (timeLeft, "Reinforcements in: " + ((this.timeLeft / (1000 * 60)) + 1)  + "m", 1000, true));
				return;
			}
			
			Entity hero = entityManager.getEntity ("hero");
			entity.addCommand (new MoveFieldCommand (entity, hero.getPosition ()));
		}
	}

	private void killShot(Entity entity2) {
		if (! won)
		{
			Command command = entity2.getCurrentCommand ();
			if (command != null)
			{
				command.stop ();
				entity2.removeCommand (command);
			}
			entity2.cleanCommands();
			entity2.deactivate ();
			entity2.addCommand (new RemoveEntityCommand (entity2));
		}
	}

	private void killAlien (Entity entity1) 
	{
		getAttackStrategy ().enemyKilled ();
		agro += agroStep;
		Command command = entity1.getCurrentCommand ();
		if (command != null)
		{
			command.stop ();
			entity1.removeCommand (command);
		}
		entity1.cleanCommands();
		entity1.deactivate ();
		entity1.addCommand (new RemoveEntityCommand (entity1));

		Entity explosion = entityManager.getPrototypeEntity ("explosion");
		explosion.addCommand (new PlaySoundCommand(Sounds.explosion));
		explosion.addCommand (new AnimationCommand (explosion, entity1.getPosition(), false));
		explosion.addCommand (new RemoveEntityCommand (explosion));
		String uniqueId = "explosion" + entity1.getName (); 
		explosion.setName (uniqueId);
		entityManager.addEntity (explosion);
	}

	private void undoCommand(Entity entity) 
	{
		Command command = entity.getCurrentCommand ();
		if (command != null)
		{
			Command unPerformCommand = command.getUndoCommand ();
			entity.removeCommand (command);
			entity.addCommand (unPerformCommand);
		}
	}
	
	public void heroInteraction (GameContainer container, float delta)
	{
		Input input = container.getInput();
		int mx = input.getMouseX ();
		int my = input.getMouseY ();
		
		Entity hero = entityManager.getEntity ("hero");
		Point position = new Point (hero.getPosition());
		
		int speed = 3;
		
		float rotation = (float) Math.toDegrees(Math.atan2 (position.x - mx+16, position.y - my+16) * -1);
		
		if (input.isMousePressed(Input.MOUSE_LEFT_BUTTON))
		{
			Random generator = new Random( Integer.MAX_VALUE );
			for (int j = 0; j < 1; ++j)
			{
				Entity shot = entityManager.getPrototypeEntity ("shot");
				shot.setPosition(0, 0);
				shot.addCommand (new SetPositionCommand (shot, position.x, position.y, false));
				hero.addCommand (new PlaySoundCommand (Sounds.laser));
				shot.addCommand (new MoveFieldCommand (shot, rotation, new Rectangle (-64, -64, 1280 + 64, 1024 + 64)));
				shot.addCommand (new RemoveEntityCommand (shot));
				String uniqueId = "shot" + container.getTime(); 
				shot.setName (uniqueId);
				entityManager.addEntity (shot);
			}
		}
		
		if (agro > 100)
			agro = 100;
		
		if (input.isMousePressed(Input.MOUSE_RIGHT_BUTTON) && agro == 100)
		{
			agro = 0;
			for (int j = 0; j < 40; ++j)
			{
				Entity shot = entityManager.getPrototypeEntity ("shot");
				shot.setPosition(0, 0);
				shot.addCommand (new SetPositionCommand (shot, position.x, position.y, false));
				
				shot.addCommand (new MoveFieldCommand (shot, j* 9 , new Rectangle (-64, -64, 1280 + 64, 1024 + 64)));
				shot.addCommand (new RemoveEntityCommand (shot));
				shot.setName ("megashot" + entityManager.getNextId ());
				entityManager.addEntity (shot);
			}
		}
		
		if (input.isKeyPressed (Input.KEY_F1))
		{
			boolean active = configManager.functionActive ("showCollisionRange");
			configManager.setFunction("showCollisionRange", active ? false : true);
		}
		if (input.isKeyPressed (Input.KEY_F2))
		{
			boolean active = configManager.functionActive ("showCollision");
			configManager.setFunction("showCollision", active ? false : true);
		}
		if (input.isKeyPressed (Input.KEY_F3))
		{
			boolean active = configManager.functionActive ("showEntityPos");
			configManager.setFunction("showEntityPos", active ? false : true);
		}
		if (input.isKeyPressed (Input.KEY_F4))
		{
			boolean active = configManager.functionActive ("showEntityRotation");
			configManager.setFunction("showEntityRotation", active ? false : true);
		}
		if (input.isKeyPressed (Input.KEY_F5))
		{
			boolean active = configManager.functionActive ("showEntityCurrentCommand");
			configManager.setFunction("showEntityCurrentCommand", active ? false : true);
		}
		if (input.isKeyPressed (Input.KEY_F6))
		{
			boolean active = configManager.functionActive ("showQuadTree");
			configManager.setFunction("showQuadTree", active ? false : true);
		}
		
		if(input.isKeyDown(Input.KEY_A))
		{
			position.x -= speed; 
		}
		
		if(input.isKeyDown(Input.KEY_D))
		{
			position.x += speed; 
		}
		
		if(input.isKeyDown(Input.KEY_W))
		{
			position.y -= speed; 
		}
		
		if(input.isKeyDown(Input.KEY_S))
		{
			position.y += speed; 
		}
		
		if ((hero.getNumberOfCommands () == 0 && hero.getCurrentCommand() == null) && (hero.getRotation () != rotation || position.x != hero.getPosition().x || position.y != hero.getPosition().y))
			hero.addCommand (new PlayerMovementCommand (hero,position, rotation));
		
	}
}